﻿using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Canteen.Core.Common
{
    public class JwtAuth : IJwtAuth
    {
        private readonly ObjectPool<RedisCache> _redisPool;
        public JwtAuth(
            ObjectPool<RedisCache> redisPool,
            IOptions<JwtAuthOptions> jwtAuthOptions)
        {
            _redisPool = redisPool ?? throw new ArgumentNullException(nameof(redisPool));
            Option = jwtAuthOptions?.Value ?? throw new ArgumentNullException(nameof(jwtAuthOptions));
        }
        public JwtAuthOptions Option { get; set; }

        public ClaimsPrincipal GetPrincipal(string Token)
        {
            var handler = new JwtSecurityTokenHandler();
            try
            {
                return handler.ValidateToken(Token, new TokenValidationParameters
                {
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Option.Secret)),
                    ValidateLifetime = false
                }, out SecurityToken validatedToken);
            }
            catch (Exception)
            {
                return null;
            }
        }
        //生成JWT字符串
        public string GenerateToken(List<Claim> Claims)
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Option.Secret));

            var token = new JwtSecurityToken(
                issuer: Option.Issuer,
                audience: Option.Audience,
                claims: Claims,
                notBefore: DateTime.UtcNow,
                expires: DateTime.UtcNow.AddDays(Option.Expires),
                signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
            );

            return new JwtSecurityTokenHandler().WriteToken(token);
        }
        // 生成刷新Token
        public string GenerateRefreshToken()
        {
            //var randomNumber = new byte[32];
            //using var rng = RandomNumberGenerator.Create();
            //rng.GetBytes(randomNumber);
            //return Convert.ToBase64String(randomNumber);
            return Guid.NewGuid().ToString().Replace("-", "");
        }

        public async Task<string> CreateRefreshToken(int UserId)
        {
            string token = GenerateRefreshToken();
            bool isSave = await RedisCache(cache => cache.StringSetAsync(token, UserId, TimeSpan.FromDays(Option.RefreshExpires)));
            if (isSave) return token;
            throw new InvalidOperationException("创建刷新Token失败");
        }
        public async Task<bool> ValidRefreshToken(string RefreshToken)
        {
            return await RedisCache(cache => cache.KeyExistsAsync(RefreshToken));
        }
        public async Task RemoveRefreshToken(string RefreshToken)
        {
            await RedisCache(cache => cache.KeyDeleteAsync(RefreshToken));
        }

        //Redis DB6
        private T RedisCache<T>(Func<IDatabase, T> func)
        {
            var redis = _redisPool.Get();
            if (redis == null) throw new InvalidOperationException("ObjectPool Redis Is Null");
            try
            {
                return func(redis.GetDatabase(6));
            }
            finally
            {
                _redisPool.Return(redis);
            }
        }
        
    }
}
